﻿using SuperX.Common.Code;
using SuperX.Common.Helper;
using SuperX.Common.Object;
using IronPython.Hosting;
using Microsoft.Scripting.Hosting;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;

namespace SuperX.ProjectCore
{
    public class PythonCode : BasicObject, ICodeScope<ScriptEngine, ScriptScope>
    {
        public const string DefaultScopeName = "DefaultScope";

        private Dictionary<string, object> _mapObject;
        private ConcurrentDictionary<string, ScriptScope> _scopes;

        public ScriptEngine PythonEngine { get; private set; }

        public Dictionary<string, object> MapObject
        {
            get => _mapObject ?? (_mapObject = new Dictionary<string, object>());
            set => _mapObject = value;
        }

        public ConcurrentDictionary<string, ScriptScope> Scopes { get => _scopes ?? (_scopes = new ConcurrentDictionary<string, ScriptScope>()); set => _scopes = value; }

        public PythonCode() : this(CodeManager.PythonEngineName)
        {
        }

        public PythonCode(string engineName) : this(engineName, null)
        {
        }

        public PythonCode(string engineName, IDictionary<string, object> variables)
        {
            InitEngine(engineName, variables);
        }

        public ScriptEngine InitEngine(string engineName)
        {
            return InitEngine(engineName, null);
        }

        public ScriptEngine InitEngine(string engineName, IDictionary<string, object> variables)
        {
            if (string.IsNullOrWhiteSpace(engineName))
                return null;
            Name = engineName;
            PythonEngine = Python.CreateEngine();
            AppentSearchPaths(PythonScriptHelper.GetScriptDllPath());
            if (variables != null)
            {
                foreach (var item in variables)
                {
                    MapObject.Add(item.Key, item.Value);
                    PythonEngine.Runtime.Globals.SetVariable(item.Key, item.Value);
                }
            }
            if (!Scopes.ContainsKey(DefaultScopeName))
            {
                var scope = PythonEngine.CreateScope(variables);
                Scopes.TryAdd(DefaultScopeName, scope);
            }

            //engine.Runtime.Globals.SetVariable(ScriptObject.Name, ScriptObject);
            return PythonEngine;
        }

        public ScriptScope CreateScope()
        {
            return PythonEngine?.CreateScope();
        }

        public ScriptScope CreateScope(IDictionary<string, object> dictionary)
        {
            return PythonEngine?.CreateScope(dictionary);
        }

        public dynamic Execute(string expression, ScriptScope scope)
        {
            return PythonEngine?.Execute(expression, scope);
        }

        public TR Execute<TR>(string expression, ScriptScope scope)
        {
            return PythonEngine == null ? default(TR) : PythonEngine.Execute<TR>(expression, scope);
        }

        public dynamic Execute(string expression)
        {
            return PythonEngine?.Execute(expression, Scopes[DefaultScopeName]);
        }

        public TR Execute<TR>(string expression)
        {
            return PythonEngine == null ? default(TR) : PythonEngine.Execute<TR>(expression, Scopes[DefaultScopeName]);
        }

        public void SetSearchPaths(List<string> dllPaths)
        {
            PythonEngine?.SetSearchPaths(dllPaths);
        }

        public void AppentSearchPaths(List<string> dllPaths)
        {
            if (PythonEngine == null)
                return;
            var paths = PythonEngine.GetSearchPaths().ToList();
            paths.AddRange(dllPaths);
            PythonEngine.SetSearchPaths(paths);
        }

        public ScriptScope GetScope(string path)
        {
            return PythonEngine?.GetScope(path);
        }

        public override void Dispose()
        {
            base.Dispose();
            if (PythonEngine != null)
            {
                foreach (var item in MapObject)
                {
                    PythonEngine.Runtime.Globals.RemoveVariable(item.Key);
                }
                PythonEngine = null;
            }
        }

        public void SetVariable(string key, object value)
        {
            Scopes.First().Value.SetVariable(key, value);
        }
    }
}